
import Mock from 'mockjs';
const {Random}  = Mock
Mock.setup({
    timeout:'200-600'
})


Mock.mock('http://81.68.131.25/api/management/article/getArticleList',{
    "articleList|10-100":[{
        "title":"@ctitle",
        "time":"@datetime",
        "tags":[Random.cword(3,6),Random.word(4,10)],
        "id":"@id"
    }]
})

Mock.mock('http://81.68.131.25/api/management/article/deleteArticle',{
    "code": 1,
    "message": "成功",
    "response": null
})

Mock.mock('http://81.68.131.25/api/management/article/editorArticle',{
    "code": 1,
    "message": "成功",
    "response": null
})

Mock.mock('http://81.68.131.25/api/management/article/createArticle',{
    "code": 1,
    "message": "成功",
    "response": null
})

Mock.mock('http://81.68.131.25/api/blog/article/getArticle',{
    "id":'@id',
    "title":"Build-Your-Own-React",
    "time":"2022-3-23",
    "abstract":'Build-Your-Own-React 项目学习地址：https://pomb.us/build-your-own-react/ 跟着作者一步一步地实现了一个react乞丐版',
    "articleTags":["React","JavaScript"],
    "body":"# Build-Your-Own-React\n" +
        "\n" +
        "## 1.react的原理复习\n" +
        "\n" +
        "### 1.jsx的基本原理\n" +
        "\n" +
        "```jsx\n" +
        "const element = <h1 title=\"foo\">Hello</h1>\n" +
        "```\n" +
        "\n" +
        "上面的代码经过babel转换后：\n" +
        "\n" +
        "```jsx\n" +
        "\n" +
        "const element = React.createElement(\n" +
        "  \"h1\",\n" +
        "  { title: \"foo\" },\n" +
        "  \"Hello\"\n" +
        ")\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "creatElement创建出以下对象\n" +
        "\n" +
        "```jsx\n" +
        "const element = {\n" +
        "    type:'h1',\n" +
        "    props:{\n" +
        "        title:'foo',\n" +
        "        children:'Hello'\n" +
        "    }\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "1. type：可以是HTMLelement元素，也可以是一个函数\n" +
        "2. props：存放写在元素中的属性，行内属性和子元素\n" +
        "3. children：元素这里是一个字符串，但是它也可以是一个数组，存放要创建元素的子元素\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "### 2.ReactDOM.render的原理\n" +
        "\n" +
        "\n" +
        "\n" +
        "```jsx\n" +
        "const element = {\n" +
        "    type:'h1',\n" +
        "    props:{\n" +
        "        title:'foo',\n" +
        "        children:'Hello'\n" +
        "    }\n" +
        "}\n" +
        "const container = document.getElementById('root')\n" +
        "//通过元素的type创建dom节点\n" +
        "const node = document.createElement(element.type)\n" +
        "//然后我们将所有元素props分配给该节点。这里只有title属性\n" +
        "node['title'] = element.props.title\n" +
        "// 创建文本节点\n" +
        "//作者：Using textNode instead of setting innerText will allow us to treat all elements in the same way later.\n" +
        "const text = document.createTextNode('')\n" +
        "text['nodeValue'] = element.props.children\n" +
        "//将创建好的文本节点放入元素\n" +
        "node.appendChild(text)\n" +
        "//将创建好的元素节点放入root\n" +
        "container.appendChild(node)\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 2.步骤一，实现creatElement\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.createElement：\n" +
        "\n" +
        "```jsx\n" +
        "function createElement(type,props,...children){\n" +
        "    return {\n" +
        "        type,\n" +
        "        props:{\n" +
        "            ...props,\n" +
        "            children\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "\n" +
        "// 使用例子:\n" +
        "createElement('div',null,a)\n" +
        "{\n" +
        "    type:'div',\n" +
        "    props:{children:[a]}\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "这里使用拓展运算符来平铺props参数，且让传入的children参数成为一个数组\n" +
        "\n" +
        "\n" +
        "\n" +
        "2.对于children的处理：\n" +
        "\n" +
        "children可以是一个对象类型也可以是文本，对于这些文本，使用一个createTextElement函数来将他们包装成一个对象,并为这个对象的type设置一个标识：'TEXT_ELEMENT'\n" +
        "\n" +
        "```js\n" +
        "function createElement(type,props,...children){\n" +
        "    return {\n" +
        "        type,\n" +
        "        props:{\n" +
        "            ...props,\n" +
        "            children:children.map(child=>typeof child==='object'?child:createElement(child))\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "\n" +
        "function createTextElement(text){\n" +
        "    return {\n" +
        "        type:'TEXT_ELEMENT',\n" +
        "        props:{\n" +
        "            nodeValue:text,\n" +
        "            children:[]\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "作者注：\n" +
        "\n" +
        "*React doesn’t wrap primitive values or create empty arrays when there aren’t `children`, but we do it because it will simplify our code, and for our library we prefer simple code than performant code.*\n" +
        "\n" +
        "```\n" +
        "当没有子代时，React 不会包装基本值或创建空数组，但我们这样做是因为它将简化代码，对于我们的库，我们更喜欢简单的代码而不是性能代码。\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "作者将这个库命名为`Didact`：\n" +
        "\n" +
        "```js\n" +
        "const Didact = {\n" +
        "    createElement\n" +
        "}\n" +
        "\n" +
        "const element = Didact.createElement(\n" +
        "    'div',\n" +
        "    {id:'foo'},\n" +
        "    Didact.createElement('a',null,'bar'),\n" +
        "    Didact.createElement('b')\n" +
        ")\n" +
        "\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "3.使用babel进行编译：\n" +
        "\n" +
        "安装babel和babel编译jsx的插件，添加上如下注释即可编译element，也可以到babel官网翻译\n" +
        "\n" +
        "```jsx\n" +
        "\n" +
        "/** @jsx Didact.createElement */\n" +
        "const element = (\n" +
        "    <div id='foo'>\n" +
        "        <a>bar</a>\n" +
        "        <b/>\n" +
        "    </div>\n" +
        ")\n" +
        "```\n" +
        "\n" +
        "我的编译结果：\n" +
        "\n" +
        "```jsx\n" +
        "/** @jsx Didact.createElement */\n" +
        "const element = Didact.createElement(\"div\", {\n" +
        "  id: \"foo\"\n" +
        "}, Didact.createElement(\"a\", null, \"bar\"), Didact.createElement(\"b\", null));\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 3.步骤二，实现render\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.先只考虑添加节点的功能，使用创建好的element对象生成dom元素，并放入container容器中\n" +
        "\n" +
        "```jsx\n" +
        "function render(element,container){\n" +
        "    // 创建dom节点\n" +
        "    const dom = document.createElement(element.type);\n" +
        "\n" +
        "    container.appendChild(dom)\n" +
        "}\n" +
        "\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "2.递归地对元素中的子元素做同样的事情：\n" +
        "\n" +
        "```jsx\n" +
        "function render(element,container){\n" +
        "    // 创建dom节点\n" +
        "    ...\n" +
        "    // 递归地为element的子元素做同样的事情\n" +
        "    element.props.children.forEach(child=>{\n" +
        "        render(child,dom)\n" +
        "    })\n" +
        "    ...\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "3.要记得处理文本元素，如果element.type==='TEXT_ELEMNT'，就要创建一个文本节点而不是一个常规的dom节点\n" +
        "\n" +
        "```jsx\n" +
        "function render(element,container){\n" +
        "\n" +
        "    \n" +
        "    // 创建dom节点\n" +
        "    const dom = element.type==='TEXT_ELEMENT'\n" +
        "        ?document.createTextNode('')\n" +
        "        :document.createElement(element.type)\n" +
        "    // 递归地为element的子元素做同样的事情\n" +
        "    element.props.children.forEach(child=>{\n" +
        "        render(child,dom)\n" +
        "    })\n" +
        "    ...\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "4.为创建好的元素元素添加上属性\n" +
        "\n" +
        "```jsx\n" +
        "function render(element,container){\n" +
        "\n" +
        "\n" +
        "    // 创建dom节点,需要判断是否为文本节点\n" +
        "    ...\n" +
        "    // 为元素添加上属性，\n" +
        "    const isProperty = key => key!=='children';\n" +
        "    Object.keys(element.props)  //获取element.props的键值\n" +
        "        .filter(isProperty)     //使用isProperty来过滤掉children属性\n" +
        "        .forEach(name=>{        //遍历过滤后的属性，为创建好的dom添加上这些属性\n" +
        "            dom[name] = element.props[name]\n" +
        "        })\n" +
        "    \n" +
        "    // 递归地为element的子元素做同样的事情\n" +
        "    ...\n" +
        "    container.appendChild(dom)\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "到这一步，这个库就有了将jsx语法渲染成dom的能力：\n" +
        "\n" +
        "完整代码：\n" +
        "\n" +
        "```jsx\n" +
        "\n" +
        "const container = document.getElementById('root');\n" +
        "const Didact = {\n" +
        "    createElement,\n" +
        "    render\n" +
        "}\n" +
        "\n" +
        "/** @jsx Didact.createElement */\n" +
        "const element = (\n" +
        "    <div id='foo'>\n" +
        "        <a href=\"#\">连接</a>\n" +
        "        <div className=\"div2\">\n" +
        "            <ul>\n" +
        "                <li>1.a</li>\n" +
        "                <li>2.b</li>\n" +
        "                <li>3.c</li>\n" +
        "            </ul>\n" +
        "        </div>\n" +
        "    </div>\n" +
        ")\n" +
        "\n" +
        "Didact.render(element,container)\n" +
        "function createElement(type,props,...children){\n" +
        "    return {\n" +
        "        type,\n" +
        "        props:{\n" +
        "            ...props,\n" +
        "            children:children.map(child=>typeof child==='object'?child:createTextElement(child))\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "// render函数\n" +
        "function render(element,container){\n" +
        "\n" +
        "\n" +
        "    // 创建dom节点,需要判断是否为文本节点\n" +
        "    const dom = element.type==='TEXT_ELEMENT'\n" +
        "        ?document.createTextNode('')\n" +
        "        :document.createElement(element.type)\n" +
        "\n" +
        "    // 为元素添加上属性，\n" +
        "    const isProperty = key => key!=='children';\n" +
        "    Object.keys(element.props)  //获取element.props的键值\n" +
        "        .filter(isProperty)     //使用isProperty来过滤掉children属性\n" +
        "        .forEach(name=>{        //遍历过滤后的属性，为创建好的dom添加上这些属性\n" +
        "            dom[name] = element.props[name]\n" +
        "        })\n" +
        "    \n" +
        "    // 递归地为element的子元素做同样的事情\n" +
        "    element.props.children.forEach(child=>{\n" +
        "        render(child,dom)\n" +
        "    })\n" +
        "    container.appendChild(dom)\n" +
        "}\n" +
        "\n" +
        "\n" +
        "//创建元素\n" +
        "function  createTextElement(text){\n" +
        "    return {\n" +
        "        type:'TEXT_ELEMENT',\n" +
        "        props:{\n" +
        "            nodeValue:text,\n" +
        "            children:[]\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 4.步骤三：实现并发模式\n" +
        "\n" +
        "\n" +
        "\n" +
        "前三步的代码存在的问题：\n" +
        "\n" +
        "当我们开始渲染时，浏览器会阻塞执行，直到render函数执行完成。而如果需要渲染的节点过于庞大，渲染这么一颗庞大的节点树需要很长的时间，这样就会阻塞浏览器的其他任务，如果浏览器需要处理其他任务，需要等到render结束，这样很显然不是很好。\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.解决方法：\n" +
        "\n" +
        "解决办法：将把工作分成小单元，在完成每个单元后，如果浏览器还有其他需要完成的事情，我们就把控制权交还给浏览器\n" +
        "\n" +
        "```jsx\n" +
        "\n" +
        "// ***************workLoop函数***************\n" +
        "\n" +
        "let nextUnitOfWork = null;\n" +
        "\n" +
        "function workLoop(deadline){\n" +
        "    let shouldYield = false;\n" +
        "    while(nextUnitOfWork&&!shouldYield){\n" +
        "        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)\n" +
        "\n" +
        "        shouldYield = deadline.timeRemaining() < 1\n" +
        "    }\n" +
        "    requestIdleCallback(workLoop)\n" +
        "}\n" +
        "\n" +
        "requestIdleCallback(workLoop)\n" +
        "\n" +
        "function performUnitOfWork(nextUnitOfWork){\n" +
        "\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "**requestIdleCallback的作用：**\n" +
        "\n" +
        "```\n" +
        "        MDN解释：\n" +
        "        window.requestIdleCallback()方法插入一个函数，这个函数将在浏览器空闲时期被调用。\n" +
        "        这使开发者能够在主事件循环上执行后台和低优先级工作，而不会影响延迟关键事件，如动画和输入响应。\n" +
        "        函数一般会按先进先调用的顺序执行，然而，如果回调函数指定了执行超时时间timeout，则有可能为了在超时前执行函数而打乱执行顺序。\n" +
        "\n" +
        "        你可以在空闲回调函数中调用requestIdleCallback()，以便在下一次通过事件循环之前调度另一个回调。\n" +
        "```\n" +
        "\n" +
        "作者：可以把它看作是一个setTimeout函数，只不过运行时机由浏览器把控而不是我们。\n" +
        "\n" +
        "`requestIdleCallback`向给与它的回调函数中传入了一个`deadline`参数，我们可以通过这个deadline参数来知道距离下次浏览器的控制还有多少时间。同时，在` workLoop`中运行了`requestIdleCallback(workLoop)`意味着只要浏览器空闲，那么就会不断地循环执行`workLoop`，所以这个函数可以监听到 ` nextUnitOfWork`的改变并做出相应的动作。\n" +
        "\n" +
        "\n" +
        "\n" +
        "**开始workloop循环**\n" +
        "\n" +
        "要开始workloop循环，就需要传入第一个工作单元，实现performUnitOfWrok,这个函数不仅要执行工作，在工作完成后还需要返回下一个工作单元\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 5.步骤四：构建Fiber树\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.在render函数中，我们创建Fiber树的根节点并且将它设置为nextUnitOfWrok。剩下的工作将在performUnitOfWork函数中进行，它会做如下三件事：\n" +
        "\n" +
        "- 添加元素到dom节点\n" +
        "- 为元素的子节点创建fiber\n" +
        "- 选择下一个工作单元\n" +
        "\n" +
        "\n" +
        "\n" +
        "2.FIber树结构：\n" +
        "\n" +
        "\n" +
        "\n" +
        "```jsx\n" +
        "Didact.render(\n" +
        "  <div>\n" +
        "    <h1>\n" +
        "      <p />\n" +
        "      <a />\n" +
        "    </h1>\n" +
        "    <h2 />\n" +
        "  </div>,\n" +
        "  container\n" +
        ")\n" +
        "```\n" +
        "\n" +
        "上文的代码结构转换为Fiber树如下：\n" +
        "\n" +
        "为了更容易地找到下一个工作单元，每一个fiber需要连接到它的第一个子元素和它的下一个兄弟元素。\n" +
        "\n" +
        "\n" +
        "\n" +
        "![Fiber Tree 2](https://pomb.us/static/c1105e4f7fc7292d91c78caee258d20d/ac667/fiber2.png)\n" +
        "\n" +
        "下一个工作单元的选择规则：\n" +
        "\n" +
        "- **当一个fiber完成时，如果检测到它有子节点，则这个节点将会作为下一个工作单元。**\n" +
        "- **如果fiber完成时它没有子节点，则将它的下一个兄弟节点作为下一个工作单元**。\n" +
        "- **如果fiber完成时没有子节点也没有兄弟节点，则往上找它的'叔叔'节点，即父节点的兄弟节点**\n" +
        "- **如果叔叔节点都没有，就继续沿着父节点网上找，直到找到某位'前辈'的兄弟，或者到达根节点**\n" +
        "- **如果真的到达根节点了，说明render函数已经执行完成**\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "3.render函数重构\n" +
        "\n" +
        "创建creatDom函数，将创建dom的逻辑从render中抽离出来。\n" +
        "\n" +
        "在render函数中，将`nextUnitOfWork`设置为fiber树的根。\n" +
        "\n" +
        "```jsx\n" +
        "// ***************创建Dom函数***************\n" +
        "\n" +
        "// 抽离出render中创建dom的逻辑，使用fiber节点来创建dom\n" +
        "\n" +
        "function createDom(fiber){\n" +
        "    // 创建dom节点,需要判断是否为文本节点\n" +
        "    const dom = fiber.type==='TEXT_ELEMENT'\n" +
        "        ?document.createTextNode('')\n" +
        "        :document.createElement(fiber.type)\n" +
        "\n" +
        "    // 为元素添加上属性，\n" +
        "    const isProperty = key => key!=='children';\n" +
        "    Object.keys(fiber.props)  //获取element.props的键值\n" +
        "        .filter(isProperty)     //使用isProperty来过滤掉children属性\n" +
        "        .forEach(name=>{        //遍历过滤后的属性，为创建好的dom添加上这些属性\n" +
        "            dom[name] = fiber.props[name]\n" +
        "        })\n" +
        "\n" +
        "    return dom\n" +
        "}\n" +
        "\n" +
        "\n" +
        "\n" +
        "// ***************render函数***************\n" +
        "\n" +
        "function render(element,container){\n" +
        "    nextUnitOfWork = {\n" +
        "        dom:container,\n" +
        "        props:{\n" +
        "            children:[element]\n" +
        "        }\n" +
        "    }\n" +
        "}\n" +
        "\n" +
        "let nextUnitOfWork = null;\n" +
        "```\n" +
        "\n" +
        "结合步骤4的`workloop`，当`nextUnitOfWork`不为空时，`workloop`监听到`nextUnitOfWork`不为空，`performUnitOfWork`函数就开始工作了。\n" +
        "\n" +
        "\n" +
        "\n" +
        "4.完成performUnitOfWork函数\n" +
        "\n" +
        "*注：这里作者将performUnitOfWork函数中的形参改成了fiber，之前是nextUnitOfWork*\n" +
        "\n" +
        "```js\n" +
        "function performUnitOfWork(fiber){\n" +
        "\n" +
        "    // 如果fiber没有dom则为其创建dom\n" +
        "    if(!fiber.dom) fiber.dom = createDom(fiber);\n" +
        "    // 如果fiber存在parent ，则要将这个fiber的dom放入parent中\n" +
        "    if(fiber.parent) fiber.parent.dom.appendChild(fiber.dom)\n" +
        "\n" +
        "    // 开始处理子节点，为每一个子节点创建新的fiber\n" +
        "    const elements = fiber.props.children\n" +
        "    let index = 0;\n" +
        "    let prevSibling = null;\n" +
        "    while(index<elements.length){\n" +
        "        const element = elements[index]\n" +
        "        const newFiber = {\n" +
        "            type:element.type,\n" +
        "            props:element.props,\n" +
        "            parent:fiber,\n" +
        "            dom:null\n" +
        "        }\n" +
        "\n" +
        "        // 如果index===0，表明它是fiber的第一个子节点\n" +
        "        if(index === 0){\n" +
        "            fiber.child = newFiber\n" +
        "        }else{\n" +
        "            prevSibling.sibling = newFiber\n" +
        "        }\n" +
        "\n" +
        "        // 记录下本次循环的newFiber，它将在下一次循环中连接它的兄弟节点\n" +
        "        prevSibling = newFiber\n" +
        "        index++\n" +
        "    }\n" +
        "\n" +
        "    // 最后，按照fiber树的寻找规则寻找下一个需要工作的工作单元\n" +
        "    // 如果有子节点就返回子节点\n" +
        "    if(fiber.child) return fiber.child\n" +
        "    // 没有子节点的情况\n" +
        "    let nexFiber = fiber\n" +
        "    while(nexFiber){\n" +
        "        // 如果有兄弟节点就返回兄弟节点\n" +
        "        if(nexFiber.sibling) return nexFiber.sibling\n" +
        "        // 没有兄弟节点就需要沿着父元素找父元素的兄弟节点\n" +
        "        nexFiber = nexFiber.parent\n" +
        "    }\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "至此Fiber树就构建完成了!\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 6.步骤五，渲染和提交阶段\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.在这里遇到了一个问题：\n" +
        "\n" +
        "在前面我们实现过workLoop函数：\n" +
        "\n" +
        "```js\n" +
        "function workLoop(deadline){\n" +
        "    let shouldYield = false;\n" +
        "    while(nextUnitOfWork&&!shouldYield){\n" +
        "        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)\n" +
        "\n" +
        "        shouldYield = deadline.timeRemaining() < 1\n" +
        "    }\n" +
        "    requestIdleCallback(workLoop)\n" +
        "}\n" +
        "\n" +
        "```\n" +
        "\n" +
        "每次获取下一个工作单元时，都需要等待浏览器的下一次空闲才能进行，如果浏览器持续繁忙，则结果是界面迟迟不出现，这显然是不合理的。\n" +
        "\n" +
        "\n" +
        "\n" +
        "解决办法：\n" +
        "\n" +
        "1. 将`performUnitOfWork`函数中的 `if(fiber.parent) fiber.parent.dom.appendChild(fiber.dom)`这个判断条件移除。这样做的目的是不让`performUnitOfWork`函数在执行时就操作dom\n" +
        "2. 添加一个参数来记录fiber树的根\n" +
        "3. 在workLoop中添加判断，当整个fiber树构建完成后，提交整个fiber树(我们已经记录下了根节点，所以提交并不是什么难事)\n" +
        "4. commitRoot函数提交后，再根据整个fiber树统一创建dom\n" +
        "\n" +
        "\n" +
        "\n" +
        "重构后的代码：\n" +
        "\n" +
        "```jsx\n" +
        "\n" +
        "// ***************commit函数***************\n" +
        "\n" +
        "function commitRoot(){\n" +
        "\t//TODO\n" +
        "}\n" +
        "// ***************render函数***************\n" +
        "\n" +
        "function render(element,container){\n" +
        " \t//记录根节点\n" +
        "    wipRoot = {\n" +
        "        dom:container,\n" +
        "        props:{\n" +
        "            children:[element]\n" +
        "        }\n" +
        "    }\n" +
        "    nextUnitOfWork = wipRoot\n" +
        "}\n" +
        "let nextUnitOfWork = null;\n" +
        "//新增wipRoot记录fiber根节点\n" +
        "let wipRoot = null\n" +
        "\n" +
        "function workLoop(deadline){\n" +
        "    let shouldYield = false;\n" +
        "    while(nextUnitOfWork&&!shouldYield){\n" +
        "        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)\n" +
        "\n" +
        "        shouldYield = deadline.timeRemaining() < 1\n" +
        "    }\n" +
        "\t//新增判断，当满足以下条件，说明fiber树构建完成\n" +
        "    if(!nextUnitOfWork&&wipRoot){\n" +
        "        //提交根节点\n" +
        "        commitRoot()\n" +
        "    }\n" +
        "    requestIdleCallback(workLoop)\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "2.commitRoot函数实现，到这一步只需要递归地将fiber树中的元素创建到dom中即可，后面还会对这个函数进行重构\n" +
        "\n" +
        "```js\n" +
        "function commitRoot(){\n" +
        "    commitWork(wipRoot.child)\n" +
        "    wipRoot = null\n" +
        "}\n" +
        "\n" +
        "function commitWork(fiber){\n" +
        "    if(!fiber) return\n" +
        "    const domParent = fiber.parent.dom\n" +
        "    domParent.appendChild(fiber.dom)\n" +
        "    commitWork(fiber.child)\n" +
        "    commitWork(fiber.sibling)\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "## 7.步骤六，协调Reconciliation\n" +
        "\n" +
        "\n" +
        "\n" +
        "到目前为止已经可以创建DOM元素了，接下来就要实现更新和移除节点的功能\n" +
        "\n" +
        "\n" +
        "\n" +
        "1.我们每次都需要需要比较新的fiber树和上一次生成的fiber树，所以需要一个参数`currentRoot`来记录当前的fiber树，同时，在根节点添加一个属性`alternate`，连接上一个提交的fiber树\n" +
        "\n" +
        "```jsx\n" +
        "//新增\n" +
        "let currentRoot = null\n" +
        "\n" +
        "function render(element,container){\n" +
        "    wipRoot = {\n" +
        "        dom:container,\n" +
        "        props:{\n" +
        "            children:[element]\n" +
        "        },\n" +
        "        alternate:currentRoot //新增\n" +
        "    }\n" +
        "    nextUnitOfWork = wipRoot\n" +
        "}\n" +
        "\n" +
        "//在commitRoot中记录currentRoot\n" +
        "function commitRoot(){\n" +
        "    commitWork(wipRoot.child)\n" +
        "    currentRoot = wipRoot\n" +
        "    wipRoot = null\n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "\n" +
        "2.在`performUnitOfWork`函数中抽离出处理子节点的逻辑代码放入函数`reconcileChildren`中\n" +
        "\n" +
        "重构后：\n" +
        "\n" +
        "```jsx\n" +
        "// ***************performUnitOfWork函数***************\n" +
        "// fiber:即nextUnitOfWork\n" +
        "function performUnitOfWork(fiber){\n" +
        "    if(!fiber.dom) fiber.dom = createDom(fiber);\n" +
        "    const elements = fiber.props.children;\n" +
        "    reconcileChildren(fiber,elements)\n" +
        "    if(fiber.child) return fiber.child\n" +
        "    let nexFiber = fiber\n" +
        "    while(nexFiber){\n" +
        "        if(nexFiber.sibling) return nexFiber.sibling\n" +
        "        nexFiber = nexFiber.parent\n" +
        "    }\n" +
        "}\n" +
        "// 分离出performUnitOfWork中的处理子节点逻辑代码\n" +
        "function reconcileChildren(wipFiber,elements){\n" +
        "\n" +
        "        let index = 0;\n" +
        "        let prevSibling = null;\n" +
        "        while(index<elements.length){\n" +
        "            const element = elements[index]\n" +
        "            const newFiber = {\n" +
        "                type:element.type,\n" +
        "                props:element.props,\n" +
        "                parent:fiber,\n" +
        "                dom:null\n" +
        "            }\n" +
        "            if(index === 0){\n" +
        "                wipFiber.child = newFiber\n" +
        "            }else{\n" +
        "                prevSibling.sibling = newFiber\n" +
        "            }\n" +
        "            prevSibling = newFiber\n" +
        "            index++\n" +
        "        }\n" +
        "    \n" +
        "}\n" +
        "```\n" +
        "\n" +
        "\n" +
        "\n" +
        "3.在`reconcileChildren`中，我们开始协调新旧fiber树的元素"
})
